_haz() {
  command -v "$1" > /dev/null 2>&1
}

human() {
    local number=${1:-0}
    local decimal
    local sizeIdx=0
    local size=(Bytes {K,M,G,T,E,P,Y,Z}B)

    while ((number > 1000)); do
        decimal="$(printf ".%02d" $((number % 1000 * 100 / 1000)))"
        number=$((number / 1000))
        (( sizeIdx++ ))
    done

    printf "$number$decimal ${size[$sizeIdx]}\n"
}


spell() {
    local candidates oldifs word array_pos
    oldifs="$IFS"
    IFS=':'

    # Parse the apsell format and return a list of ":" separated words
    read -a candidates <<< "$(printf "%s\n" "$1" \
        | aspell -a \
        | awk -F':' '/^&/ {
            split($2, a, ",")
            result=""
            for (x in a) {
                gsub(/^[ \t]/, "", a[x])
                result = a[x] ":" result
            }
            gsub(/:$/, "", result)
            print result
        }')"

    # Reverse number and print the parsed bash array because the list comes
    # out of gawk backwards
    for item in "${candidates[@]}"; do
        printf '%s\n' "$item"
    done \
        | tac \
        | nl \
        | less -FirSX

    printf "[ $(tput setaf 2)?$(tput sgr0) ]\t%s" \
        'Enter the choice (empty to cancel, 0 for input): '
    read index

    [[ -z "$index" ]] && return
    [[  "$index" == 0 ]] && word="$1"

    [[ -z "$word" ]] && {
        array_pos=$(( ${#candidates[@]} - index ))
        word="${candidates[$array_pos]}"
    }

    [[ -n "$word" ]] && {
        printf "$word" | xsel -p
        printf "Copied '%s' to clipboard!\n" "$word"
    } || printf "[ $(tput setaf 1):($(tput sgr0) ] %s\n" 'No match found'


    IFS="$oldifs"
}

# Copy w/ progress
cp_p () {
  rsync -WavP --human-readable --progress $1 $2
}

# All the dig info
function digga() {
    dig +nocmd "$1" any +multiline +noall +answer
}

# Simple calculator
function calc() {
    local result=""
    result="$(printf "scale=10;$*\n" | bc --mathlib | tr -d '\\\n')"
    #                       └─ default (when `--mathlib` is used) is 20
    #
    if [[ "$result" == *.* ]]; then
        # improve the output for decimal numbers
        printf "$result" |
        sed -e 's/^\./0./'        `# add "0" for cases like ".5"` \
            -e 's/^-\./-0./'      `# add "0" for cases like "-.5"`\
            -e 's/0*$//;s/\.$//'   # remove trailing zeros
    else
        printf "$result"
    fi
    printf "\n"
}

findalias () {
    unset file
    grep -HiERn "alias ${1}=.*" /etc/bashrc ~/.${MYSHELL}rc $HOME/.exports $HOME/.aliases $HOME/.functions $HOME/.extra 
}

### User functions ###
extract () {
     if [ -f $1 ] ; then
         case $1 in
             *.tar.bz2)   tar xjf $1        ;;
             *.tar.gz)    tar xzf $1     ;;
             *.bz2)       bunzip2 $1       ;;
             *.rar)       rar x $1     ;;
             *.gz)        gunzip $1     ;;
             *.tar)       tar xf $1        ;;
             *.tbz2)      tar xjf $1      ;;
             *.tgz)       tar xzf $1       ;;
             *.zip)       unzip $1     ;;
             *.Z)         uncompress $1  ;;
             *.7z)        7z x $1    ;;
             *)           echo "'$1' cannot be extracted via extract()" ;;
         esac
     else
         echo "'$1' is not a valid file"
     fi
}

g() {
  if [ $# -gt 0 ]; then
    git "$@"
  else
    git hist3
  fi
}

gco() {
  if [ $# -gt 0 ]; then
    git checkout "$@"
  else
    git branch
  fi
}

# Start an HTTP server from a directory, optionally specifying the port
function server() {
    local port="${1:-8000}"
    sleep 1 && open "http://localhost:${port}/" &
    # Set the default Content-Type to `text/plain` instead of `application/octet-stream`
    # And serve everything as UTF-8 (although not technically correct, this doesn’t break anything for binary files)
    python -c $'import SimpleHTTPServer;\nmap = SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map;\nmap[""] = "text/plain";\nfor key, value in map.items():\n\tmap[key] = value + ";charset=UTF-8";\nSimpleHTTPServer.test();' "$port"
}

# Display image with tput
function image() {
    convert $1 -resize 40 txt:-|sed -E 's/://;s/\( ? ?//;s/, ? ?/,/g;s/\)//;s/([0-9]+,[0-9]+,[0-9]+),[0-9]+/\1/g;s/255/254/g;/mage/d'|awk '{print $1,$2}'|sed -E 's/^0,[0-9]+ /print "echo;tput setaf "\;/;s/^[0-9]+,[0-9]+ /print "tput setaf ";/;s/(.+),(.+),(.+)/\1\/42.5*36+\2\/42.5*6+\3\/42.5+16/'|bc|sed 's/$/;echo -n "  ";/'|tr '\n' ' '|sed 's/^/tput rev;/;s/; /;/g;s/$/tput sgr0;echo/'|bash
}

# Pengwynn gxpr https://raw.github.com/pengwynn/dotfiles/ba9efd3d3073314d52d891e9f32b2ca6d26d5d3a/bin/gxpr
# Usage: gxpr <expression>
# Like expr(1), but uses Google's calculator to evaluate <expression>.
#
# Math examples:
#   $ gxpr '1 + 1'
#   2
#
#   $ gxpr 2 ^  16
#   65536
#
#   $ gxpr '(2 ^ 1) + (2 ^ 2) + (2 ^ 3) + (2 ^ 5)'
#   46
#
#   $ gxpr '5*9+(sqrt 10)^3='
#   76.6227766
#
# Conversion examples:
#   $ gxpr 1GB in KB
#   1048576 kilobytes
#
#   $ gxpr 10 megabits in megabytes
#   1.25 megabytes
#
#   $ gxpr 2 miles in inches
#   126720 inches

function gxpr() {
    local CURL_PATH=$(which curl)
    local CURL="$CURL_PATH -s --header 'User-Agent:gxpr/1.0'"
    local SEARCH="http://www.google.com/search"
    local EXPR=$(echo "$@" | sed -e 's/+/%2B/g' -e 's/ /+/g')
    local PAGE=$(curl -s --header 'User-Agent:gxpr/1.0' "http://www.google.com/search?q=$EXPR")

    res=$(
        echo $PAGE |
        perl -ne '/<h2 class="r".*?>(.*)<\/h2>/ and print $1' |
        perl -ne '/= (.*)/ and print $1' |
            perl -pe 's/[^\x00-\x7F]//g'
    )

    # if we don't have a result, assume it's an invalid expression
    test -z "$res" && {
        echo "invalid expression:" "$@" 1>&2
    }

    echo "$res"
}

function psgrep() {
  ps axuf | grep -v grep | grep "$@" -i --color=auto
}

function fname() {
  find . -iname "*$@*"
}

function take() {
  mkdir $1 && cd $1
}

speedread () {
  (while read line; do
    for word in $line; do
      clear
      for i in $(seq 0 $(( LINES / 2 - 4 ))); do
        printf "\n"
      done
      figlet -c -w $COLUMNS $word
      sleep 0.2
    done
  done
  printf "\n") < "$1"
}

# http://notary.icsi.berkeley.edu/
bdns() {
    sum=$(openssl s_client -connect ${1}:443 < /dev/null 2>/dev/null | openssl x509 -outform DER | openssl sha1 | awk '{print $2}')
    echo "$sum"
    dig +short txt ${sum}.notary.icsi.berkeley.edu
}

# http://jeroenjanssens.com/2013/08/16/quickly-navigate-your-filesystem-from-the-command-line.html
jump () {
    cd -P "$MARKPATH/$1" 2>/dev/null || echo "No such mark: $1"
}

mark() {
    local markdirname="$(dirname $1)"
    if [[ "$markdirname" != "." ]]; then
        mkdir -p "$MARKPATH/${markdirname}"
    else
        mkdir -p "$MARKPATH"
    fi

    ln -s "$(pwd)" "$MARKPATH/$1"
}

unmark() {
    rm -i "$MARKPATH/$1"
}

marks() {
    ls -l "$MARKPATH" | sed 's/  / /g' | cut -d' ' -f9- | sed 's/ -/\t-/g' && echo
}

# autocomplete marks
if _haz compctl; then
    _completemarks() {
          reply=($(ls $MARKPATH))
    }

    compctl -K _completemarks jump
    compctl -K _completemarks unmark

fi

if _haz complete; then
    _completemarks() {
      local curw=${COMP_WORDS[COMP_CWORD]}
      local wordlist=$(find $MARKPATH -type l -printf "%f\n")
      COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
      return 0
    }

    complete -F _completemarks jump unmark

fi

# note () {
#   if (( $# == 0 )); then
#     local fn="${_N_PATH:=$HOME/.notes}"
#   else
#     local fn="${_N_PATH:=$HOME/.notes}/${@}.note"
#   fi
# 
#   mkdir -p "${_N_PATH:=$HOME/.notes}"; vim --cmd "autocmd VimLeave *.note :! bash -ci \"gist %\" &> /dev/null" -- "$fn";
# }

gist () {
  if [[ "$1" == *.note ]]; then
    local _prename="$(basename $1)"
    local name="[NOTE] ${_prename%.*}.md"
  else
    local _prename="$(basename $1)"
    local name="${_prename}"
  fi
  local filename="/tmp/gist-$(date +%s)";

  echo "{\"description\":\"$(basename ${1})\",\"public\":\"false\",\"files\":{\"${name}\":{\"content\":\"" > "$filename"
  cat "$1" | tr '"' "'" | sed ':a;N;$!ba;s/\n/\\n/g' >> "$filename"
  echo "\"}}" >> "$filename"

  curl -H "Authorization: token $GITHUB_TOKEN" -X POST --data @"$filename" https://api.github.com/gists

  \rm -- "$filename"
}

# Conversion
ascii2hex () {
  printf "$@" | xxd -p -c1 -u
}

hex2bin () {
  hex=$(echo "$1" | tr '[:lower:]' '[:upper:]')
  echo "ibase=16; obase=2; $hex" | bc | awk '{printf "%08s\n", $0}'
}

bin2hex () {
  echo "ibase=2; obase=16; $1" | bc
}

ascii2bin () {
  while read hex; do
    echo "ibase=16; obase=2; $hex" | bc | awk '{printf "%08s\n", $0}'
  done < <(printf "$@" | xxd -p -c1 -u)
}

# Decode \x{ABCD}-style Unicode escape sequences
function unidecode() {
  perl -e "binmode(STDOUT, ':utf8'); print \"$@\"";
  # print a newline unless we’re piping the output to another program
  if [ -t 1 ]; then
    echo ""; # newline
  fi;
}

# Get a character’s Unicode code point
function codepoint() {
  perl -e "use utf8; print sprintf('U+%04X', ord(\"$@\"))";
  # print a newline unless we’re piping the output to another program
  if [ -t 1 ]; then
    echo ""; # newline
  fi;
}

# Show all the names (CNs and SANs) listed in the SSL certificate
# for a given domain
function getcertnames() {
  if [ -z "${1}" ]; then
    echo "ERROR: No domain specified.";
    return 1;
  fi;
  local domain="${1}";
  echo "Testing ${domain}…";
  echo ""; # newline

  local tmp=$(echo -e "GET / HTTP/1.0\nEOT" \
    | openssl s_client -connect "${domain}:443" 2>&1);

  if [[ "${tmp}" = *"-----BEGIN CERTIFICATE-----"* ]]; then
  local certText=$(echo "${tmp}" \
    | openssl x509 -text -certopt "no_header, no_serial, no_version, \
    no_signame, no_validity, no_issuer, no_pubkey, no_sigdump, no_aux");
    echo "Common Name:";
    echo ""; # newline
    echo "${certText}" | grep "Subject:" | sed -e "s/^.*CN=//";
    echo ""; # newline
    echo "Subject Alternative Name(s):";
    echo ""; # newline
    echo "${certText}" | grep -A 1 "Subject Alternative Name:" \
      | sed -e "2s/DNS://g" -e "s/ //g" | tr "," "\n" | tail -n +2;
    return 0;
  else
    echo "ERROR: Certificate not found.";
    return 1;
  fi;
}

read_mime() {
  local file="$1"
  xdg-mime query filetype "$file" 2>/dev/null | cut -d ';' -f 1
}

wiki() {
  local keyword=$(printf "$1" | perl -pe's/([^-_.~A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg')
  dig +short txt "$keyword.wp.dg.cx"
}

tc () {
  local _start_time=$(date +%s)

  export TERM="screen-256color"

  if command -v mux; then
    mux start "$1"
  elif command -v tmux; then
    tmux new -s "$1"
  else
    unset _start_time
    printf "$(tput setaf 1)[ERR]$(tput sgr0) Tmux not found\n"
  fi

  local _end_time=$(date +%s)
  local _total_time=$(( _end_time - _start_time ));
  if ((_end_time == _start_time || _start_time == 0)); then
    _total_time=0
  fi

  (( $_total_time < 5 )) && return
  local days=$(( $_total_time / 60 / 60 / 24 ))
  local hours=$(( $_total_time / 60 / 60 % 24 ))
  local minutes=$(( $_total_time / 60 % 60 ))
  local seconds=$(( $_total_time % 60 ))
  echo "$(date -R) - ${days}d ${hours}h ${minutes}m ${seconds}s" >> "$HOME/.work_log"

  unset days hours minutes seconds _total_time _start_time _end_time
}

# ZShell Completions
if _haz compctl; then
    _completenotes() {
      reply=($(ls "${_N_PATH:=$HOME/.notes}"))
    }

    compctl -K _completenotes n
# Bash Completions
elif _haz complete; then
  _completenotes() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local wordlist=$(find ${_N_PATH:=$HOME/.notes} -type f -name '*.note' -printf "%f\n")
    COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$cur"))
    return 0
  }

  complete -F _completenotes n
fi

# find all files under current dir with no eol on last line
findnoeol() {
    pcregrep -LMr '\n$' . | grep -v .git | less -FirSX
}

h() {
    grep -i $1 ~/.muh_history
}

# List files provided by debian packages
# http://superuser.com/a/904419
deb-files() {
    local os=$(lsb_release -si | sed -e 's Ubuntu http://packages.ubuntu.com/ ' -e 's Debian https://packages.debian.org/ ')
    local release=$(lsb_release -sc)
    local package=$1
    local list=$(curl -q -s "${os}/${release}/all/${package}/filelist")
    echo "$list" | sed -n -e '/<pre>/,/<\/pre>/p' | sed -e 's/<[^>]\+>//g' -e '/^$/d';
}

deb-grep() {
    local package=$1
    local grep_args=$2
    for file in $(deb-files "$package"); do
        if dpkg-query -W "$package" &> /dev/null; then
            found=$(strings "$file" | grep $grep_args)
            if [ -n "$found" ]; then
                printf '\n\n[ %s ]\n' "$file"
                printf "$found"
            fi
        else
            echo "Package $package not installed"
        fi
    done
}

thousands() {
    awk '{ printf("%'"'"'d\n",$1); }'
}
